package models

import org.mongodb.scala.bson._
import play.api.libs.json._

import scala.language.implicitConversions

/**
 * Mongodb数据类型翻译
 *
 * @author Corbin
 */
case class Translated[T](self: T) extends AnyVal

object Translated {

  //def apply[A](f: JsPath ⇒ Reads[String] ⇒ Reads[A]): Reads[A] =

  def read[T, T1](key: String)(
      implicit fmt: Format[T],
      fmt1: Format[T1],
      convert: T ⇒ T1
  ): Reads[T1] =
    (__ \ key).read[T].map(convert)

  def read1[T1](key: String)(
      implicit fmt: Format[String],
      fmt1: Format[T1],
      convert: String ⇒ T1
  ): Reads[T1] =
    (__ \ key).read[String].map(convert)

  def readNullable[T1](key: String)(
      implicit fmt: Format[String],
      fmt1: Format[T1],
      convert: Option[String] ⇒ Option[T1]
  ): Reads[Option[T1]] =
    (__ \ key).readNullable[String].map(convert)

  def jsonReads[T, T1](key: String, convert: T ⇒ T1)(
      implicit fmtT: Format[T],
      fmtT1: Format[T1]
  ): Reads[T1] =
    (__ \ key).read[T].map(convert)

  def jsonFormat[T, T1](key: String, convert: T ⇒ T1)(
      implicit fmtT: Format[T],
      fmtT1: Format[T1]
  ): OFormat[T1] = OFormat[T1](
    jsonReads[T, T1](key, convert),
    Writes.at(__ \ key)(fmtT1)
  )

  def jsonReads1[T](key: String, convert: String ⇒ T)(
      implicit fmtT: Format[String],
      fmtT1: Format[T]
  ): Reads[T] =
    (__ \ key).read[String].map(convert)

  //  def jsonRead2[T, T1](key: String, convert: T ⇒ T1)(
  //    implicit fmtT: Format[T], fmtT1: Format[T1]
  //  ): Reads[T1] = {
  //    Reads[T1] { json ⇒
  //
  //    }
  //    Reads.at(__ \ key)(fmtT).map {
  //      case None    ⇒ (__ \ key).read[T].map(convert) //Reads(_ ⇒ JsSuccess(new ObjectId(), __ \ key))
  //      case Some(_) ⇒ (__ \ key).read[T].map(convert)
  //    }
  //  }

  def readWithDef[T, T1](key: String, default: ⇒ Option[T1])(convert: T ⇒ Option[T1])(
      implicit fmtT: Format[T],
      fmtT1: Format[T1]
  ): Reads[Option[T1]] =
    Reads[Option[T1]] { json =>
      (__ \ key)
        .applyTillLast(json)
        .fold(
          identity,
          _.fold(
            _ ⇒ JsSuccess(default), {
              case JsNull ⇒ JsSuccess(default)
              case js     ⇒ fmtT.reads(js).repath(__ \ key).map(convert)
            }
          )
        )
    }

  def readObjectId(key: String)(
      implicit fmtT: Format[String],
      fmtT1: Format[BsonObjectId]
  ): Reads[BsonObjectId] =
    jsonReads(key, { oid: String ⇒
      BsonObjectId(oid)
    })

  def readObjectId2(key: String)(
      implicit fmtT: Format[String],
      fmtT1: Format[BsonObjectId]
  ): Reads[Option[BsonObjectId]] =
    readWithDef[String, BsonObjectId](key, Some(BsonObjectId()))({ oid: String ⇒
      Some(BsonObjectId(oid))
    })

  def writeObjectId(key: String): OWrites[BsonObjectId] =
    (__ \ key).write[BsonObjectId](Writes[BsonObjectId](obj ⇒ JsString(obj.toString)))

  object implicits {

    implicit val str2ObjectId: String ⇒ ObjectId         = hexString ⇒ new ObjectId(hexString)
    implicit val str2BsonObjectId: String ⇒ BsonObjectId = hexString ⇒ BsonObjectId(hexString)

    implicit val str2ObjectIdOpt: Option[String] ⇒ Option[ObjectId] =
      hexString ⇒ Some(hexString.fold(new ObjectId)(value ⇒ new ObjectId(value)))
    implicit val str2BsonObjectIdOpt: Option[String] ⇒ Option[BsonObjectId] =
      hexString ⇒ Some(hexString.fold(BsonObjectId())(value ⇒ BsonObjectId(value)))
  }

}
